package bma.common.thrift.ai;

import java.util.Map;

import org.apache.thrift.TApplicationException;
import org.apache.thrift.TBase;
import org.apache.thrift.TException;
import org.apache.thrift.TProcessor;
import org.apache.thrift.protocol.TMessage;
import org.apache.thrift.protocol.TMessageType;
import org.apache.thrift.protocol.TProtocol;
import org.apache.thrift.protocol.TProtocolException;
import org.apache.thrift.protocol.TProtocolUtil;
import org.apache.thrift.protocol.TType;

import bma.common.langutil.ai.stack.AIStack;
import bma.common.langutil.ai.stack.AIStackAbstract;
import bma.common.thrift.TProcessor4Error;

public abstract class TAIBaseProcessor<IFACE> extends TProcessor4Error
		implements TProcessor, TAIProcessor {

	final org.slf4j.Logger log = org.slf4j.LoggerFactory
			.getLogger(TAIBaseProcessor.class);

	protected final IFACE iface;
	protected final Map<String, TAIProcessFunction<IFACE, ? extends TBase>> processMap;

	protected TAIBaseProcessor(
			IFACE iface,
			Map<String, TAIProcessFunction<IFACE, ? extends TBase>> processFunctionMap) {
		this.iface = iface;
		this.processMap = processFunctionMap;
	}

	public IFACE getIFace() {
		return iface;
	}

	public TAIProcessFunction<IFACE, ? extends TBase> queryProcessFunction(
			String name) {
		if (this.processMap == null)
			return null;
		return this.processMap.get(name);
	}

	@Override
	public boolean process(TProtocol in, TProtocol out) throws TException {
		TMessage msg = in.readMessageBegin();
		TAIProcessFunction fn = queryProcessFunction(msg.name);
		if (fn == null) {
			TProtocolUtil.skip(in, TType.STRUCT);
			in.readMessageEnd();
			TApplicationException x = new TApplicationException(
					TApplicationException.UNKNOWN_METHOD,
					"Invalid method name: '" + msg.name + "'");
			out.writeMessageBegin(new TMessage(msg.name,
					TMessageType.EXCEPTION, msg.seqid));
			x.write(out);
			out.writeMessageEnd();
			out.getTransport().flush();
			return true;
		}
		aiprocess(msg, in, out, fn);
		return true;
	}

	protected void aiprocess(final TMessage msg, TProtocol iprot,
			final TProtocol oprot, TAIProcessFunction fn) throws TException {
		TBase args = fn.queryEmptyArgsInstance();
		try {
			args.read(iprot);
		} catch (TProtocolException e) {
			iprot.readMessageEnd();
			TApplicationException x = new TApplicationException(
					TApplicationException.PROTOCOL_ERROR, e.getMessage());
			oprot.writeMessageBegin(new TMessage(fn.getMethodName(),
					TMessageType.EXCEPTION, msg.seqid));
			x.write(oprot);
			oprot.writeMessageEnd();
			oprot.getTransport().flush();
			return;
		}
		iprot.readMessageEnd();

		final String methodName = fn.getMethodName();
		AIStackAbstract<TBase> stack = new AIStackAbstract<TBase>() {

			@Override
			public boolean success(TBase result) {
				try {
					if (result != null) {
						oprot.writeMessageBegin(new TMessage(methodName,
								TMessageType.REPLY, msg.seqid));
						result.write(oprot);
						oprot.writeMessageEnd();
						oprot.getTransport().flush();
					}
				} catch (Exception e) {
					if (log.isWarnEnabled()) {
						log.warn("write result(" + result + ") fail", e);
					}
				}
				return true;
			}

			@Override
			public boolean failure(Throwable t) {
				if (log.isErrorEnabled()) {
					log.error("process (" + msg.name + ") fail", t);
				}
				handleException(t, msg, oprot);
				return true;
			}

			@Override
			public AIStack<?> getParent() {
				return null;
			}

		};
		try {
			fn.getResult(iprot, oprot, stack, iface, args);
		} catch (Exception e) {
			if (log.isErrorEnabled()) {
				log.error("getResult (" + msg.name + ") fail", e);
			}
			handleException(e, msg, oprot);
		}
	}
}
